#!/usr/bin/env bash
# Copyright (c) 2021 maminjie <maminjie2@huawei.com>
# SPDX-License-Identifier: MulanPSL-2.0

method_def provides requires whatprovides whatrequires
method_def requiredby builddep

# ship_get_bins_fromobs prj pkg
ship_get_bins_fromobs() {
    local bins=""
    local prj=$1
    local pkg=$2
    local results=$(osc results "$prj" "$pkg" 2> /dev/null)
    local repo_arch=$(echo "$results" | awk '{print $1"/"$2}' | grep "aarch64") # default is aarch64
    if [ -n "$repo_arch" ]; then
        # /build/project/repo/arch/package
        local content="/build/$prj/$repo_arch/$pkg"
        bins=$(osc api -X GET "$content" 2> /dev/null | grep ".rpm" | awk '{print $2}' | awk -F '=' '{print $2}' | sed 's/"//g' | sed 's/\.rpm$//g' | uniq)
        bins=$(dnf_get_pkgname "$bins" | uniq)
    fi
    echo "$bins"
}

__usage_params() {
    echo "params:"
    echo "    ... - dnf global options, like: --repo reponame"
}


usage_provides() {
    echo "provides (pro): Display capabilities provided by the package"
    echo ""
    echo "usage:"
    echo "    ${PROG} pro PROJECT PACKAGE ...      # obs PROJECT and PACKAGE"
    echo "    ${PROG} pro PACKAGEs ...             # rpm PACKAGE names"
    echo ""
    __usage_params
    echo ""
    echo "examples:"
    echo "    1) Iterate the rpm packages under openEuler:Mainline/dnf (aarch64 default)"
    echo ""
    echo "        ${PROG} pro openEuler:Mainline dnf --repo mainline"
    echo ""
    echo "    2) Display dnf's sub-package python3-dnf"
    echo ""
    echo "        ${PROG} pro python3-dnf --repo mainline"
    echo ""
    echo "    3) Display dnf's sub-package python3-dnf and dnf"
    echo ""
    echo "        ${PROG} pro \"python3-dnf dnf\" --repo mainline"
    echo ""
    echo "    4) Display dnf's sub-packages those are in file (one per line or space separated)"
    echo ""
    echo "        ${PROG} pro xx.txt --repo mainline"
    echo ""
}

# __do_provides pkgs
__do_provides() {
    printf "%s,%s\n" "# Package" "# Provides"
    for pkg in $pkgs; do
        local pros=$(dnf_get_provides "$pkg")
        for pro in $pros; do
            printf "%s,%s\n" "$pkg" "$pro"
        done
    done
}

alias_def provides pro
# do_provides pkgs ...
# do_provides prj pkg ...
do_provides() {
    local pkgs=""
    local prj=""
    local pkg=""
    if [ $# -lt 1 ]; then
        usage_provides; exit
    elif [ $# -ge 2 ]; then
        # Quotation marks cannot be added to regular expressions
        if [[ "$2" =~ ^- ]]; then
            pkgs=$(get_list "$1"); shift 1
        else
            prj=$1; pkg=$2; shift 2
        fi
    else
        pkgs=$(get_list "$1"); shift 1
    fi
    dnf_set_global_option "$@"
    if [ -n "$prj" ]; then
        pkgs=$(ship_get_bins_fromobs "$prj" "$pkg")
    fi
    __do_provides "$pkgs" | format_column ','
}

usage_requires() {
    echo "requires (req): Display capabilities that the package install depends on"
    echo ""
    echo "usage:"
    echo "    ${PROG} req PACKAGE ..."
    echo ""
    __usage_params
    echo ""
}

# __do_requires reqs
__do_requires() {
    local reqs="$1"
    local srcs=()
    printf "%s,%s,%s,%s\n" "# Package" "# Requires" "# Requires-bin" "# Requires-src"
    for req in $reqs; do
        local bin=$(dnf_get_whatprovides "$req")
        local src="Not-Found"
        if [ -z "$bin" ]; then
            bin="Not-Found"
        else
            if [[ "$bin" =~ \.src$ ]]; then
                src=$(dnf_get_pkgname "$bin")
            else
                bin=$(dnf_get_pkgname "$bin")
                src=$(dnf_get_source "$bin")
            fi
            if [ -z "$src" ]; then
                src=$bin
            fi
        fi
        array_add srcs "$src"
        printf "%s,%s,%s,%s\n" "$pkg" "$req" "$bin" "$src"
    done
    printf ",,,\n"
    echo "# Requires Package"
    array_uniq srcs
}

alias_def requires req
# do_requires pkg ...
do_requires() {
    local pkg=""
    if [ $# -lt 1 ]; then
        usage_requires; exit
    else
        pkg=$1; shift 1
    fi
    dnf_set_global_option "$@"
    local reqs=$(dnf_get_requires "$pkg")
    __do_requires "$reqs" | format_column ','
}

usage_whatprovides() {
    echo "whatprovides (wpro): Show only results that provide REQ"
    echo ""
    echo "usage:"
    echo "    ${PROG} wpro REQ ..."
    echo ""
    __usage_params
    echo ""
}

alias_def whatprovides wpro
# do_whatprovides req ...
do_whatprovides() {
    local req=""
    if [ $# -lt 1 ]; then
        usage_whatprovides; exit
    else
        req=$1; shift 1
    fi
    dnf_set_global_option "$@"
    local bins=$(dnf_get_whatprovides "$req")
    if [ -z "$bins" ]; then
        echo "Not Found package that provides \"$req\""; exit
    fi
    {
    printf "%s,%s,%s\n" "# Provides" "# Binary" "# Source"
    for bin in $bins; do
        local src=""
        if [[ "$bin" =~ \.src$ ]]; then
            src=$(dnf_get_pkgname "$bin")
        else
            bin=$(dnf_get_pkgname "$bin")
            src=$(dnf_get_source "$bin")
        fi
        printf "%s,%s,%s\n" "$req" "$bin" "$src"
    done
    } | format_column ','
}

usage_whatrequires() {
    echo "whatrequires (wreq): Shows results that requires package provides and files"
    echo ""
    echo "usage:"
    echo "    ${PROG} wreq REQ ..."
    echo ""
    __usage_params
    echo ""
}

alias_def whatrequires wreq
# do_whatrequires req
do_whatrequires() {
    local req=""
    if [ $# -lt 1 ]; then
        usage_whatrequires; exit
    else
        req=$1; shift 1
    fi
    dnf_set_global_option "$@"
    local bins=$(dnf_get_whatrequires "$req")
    {
        printf "%s,%s,%s\n" "# Symbol" "# Requires-bin" "# Requires-src"
        for bin in $bins; do
            local src=""
            if [[ "$bin" =~ \.src$ ]]; then
                src=$(dnf_get_pkgname "$bin")
            else
                bin=$(dnf_get_pkgname "$bin")
                src=$(dnf_get_source "$bin")
            fi
            printf "%s,%s,%s\n" "$req" "$bin" "$src"
        done
    } | format_column ','
}


usage_requiredby() {
    echo "requiredby (reqb): Analyse the required packages"
    echo ""
    echo "usage:"
    echo "    ${PROG} reqb PROJECT PACKAGE ...    # obs PROJECT and PACKAGE"
    echo "    ${PROG} reqb PACKAGEs ...           # rpm PACKAGE names"
    echo ""
    __usage_params
    echo ""
    echo "examples:"
    echo "    1) Iterate the rpm packages under openEuler:Mainline/dnf (aarch64 default)"
    echo ""
    echo "        ${PROG} reqb openEuler:Mainline dnf --repo mainline"
    echo ""
    echo "    2) Analyze dnf's sub-package python3-dnf"
    echo ""
    echo "        ${PROG} reqb python3-dnf --repo mainline"
    echo ""
    echo "    3) Analyze dnf's sub-package python3-dnf and dnf"
    echo ""
    echo "        ${PROG} reqb \"python3-dnf dnf\" --repo mainline"
    echo ""
    echo "    4) Analyze dnf's sub-packages those are in file (one per line or space separated)"
    echo ""
    echo "        ${PROG} reqb xx.txt --repo mainline"
    echo ""
    echo "format:"
    echo "    One way to format analysis results is:"
    echo "        1) ${PROG} reqb python3-dnf --repo mainline | tee res.txt"
    echo "        2) cat res.txt | column -t -s ','"
    echo ""
}

# __do_requiredby pkgs
__do_requiredby() {
    local pkgs="$1"
    local rbpkgs=()

    printf "%s,%s,%s,%s\n" "# Package" "# Provides" "# RequiredBy-bin" "# RequiredBy-src"
    for pkg in $pkgs; do
        local pros=$(dnf_get_provides "$pkg")
        for pro in $pros; do
            local reqbins=$(dnf_get_whatrequires "$pro")
            for bin in $reqbins; do
                local reqsrc=""
                if [[ "$bin" =~ \.src$ ]]; then
                    reqsrc=$(dnf_get_pkgname "$bin")
                else
                    bin=$(dnf_get_pkgname "$bin")
                    reqsrc=$(dnf_get_source "$bin")
                fi
                if [ -z "$reqsrc" ]; then
                    continue
                fi
                printf "%s,%s,%s,%s\n" "$pkg" "$pro" "$bin" "$reqsrc"
                array_add rbpkgs "$reqsrc"
            done
        done
    done
    printf ",,,\n"
    printf "# RequiredBy:\n"
    array_uniq rbpkgs
}

alias_def requiredby reqb
# do_requiredby prj pkg ...
# do_requiredby pkgs ...
do_requiredby() {
    local pkgs=""
    local prj=""
    local pkg=""
    if [ $# -lt 1 ]; then
        usage_requiredby; exit
    elif [ $# -ge 2 ]; then
        # Quotation marks cannot be added to regular expressions
        if [[ "$2" =~ ^- ]]; then
            pkgs=$(get_list "$1"); shift 1
        else
            prj=$1; pkg=$2; shift 2
        fi
    else
        pkgs=$(get_list "$1"); shift 1
    fi
    dnf_set_global_option "$@"
    if [ -n "$prj" ]; then
        pkgs=$(ship_get_bins_fromobs "$prj" "$pkg")
    fi
    __do_requiredby "$pkgs" # | format_column ','
}

# __do_installdep ia oa
__do_installdep() {
    local iarray=($(array_all $1))
    local oarray=$2

    while [ ${#iarray[@]} != 0 ]; do
        local deps=()
        for pkg in ${iarray[@]}; do
            local srcs=()
            local syms=$(dnf_get_requires "$pkg")
            for sym in $syms; do
                local bin=$(dnf_get_whatprovides "$sym")
                local src=""
                if [ -z "$bin" ]; then
                    continue
                else
                    if [[ "$bin" =~ \.src$ ]]; then
                        src=$(dnf_get_pkgname "$bin")
                    else
                        bin=$(dnf_get_pkgname "$bin")
                        src=$(dnf_get_source "$bin")
                    fi
                    if [ -z "$src" ]; then
                        src=$bin
                    fi
                fi
                printf "%s,%s,%s,%s\n" "$pkg" "$sym" "$bin" "$src"
                array_add srcs "$src"
            done
            array_extend deps srcs
        done
        iarray=($(array_uniq deps))
        local tmp=()
        for pkg in ${iarray[@]}; do
            if [ $(array_exists $oarray $pkg) ]; then
                continue
            fi
            tmp=(${tmp[@]} ${array[$j]})
        done
        if [ ${#tmp[@]} != 0 ]; then
            array_extend $oarray tmp
        fi
        iarray=(${tmp[@]})
    done
}

# __do_builddep ia oa
__do_builddep() {
    local iarray=($(array_all "$1"))
    local oarray="$2"
    local deps=()
    for ((i = 0; i < ${#iarray[@]}; i++)); do
        local pkg=${iarray[$i]}
        local srcs=()
        local syms=$(dnf_get_requires "$pkg")
        for sym in $syms; do
            local bin=$(dnf_get_whatprovides "$sym")
            local src=""
            if [ -z "$bin" ]; then
                continue
            else
                if [[ "$bin" =~ \.src$ ]]; then
                    src=$(dnf_get_pkgname "$bin")
                else
                    bin=$(dnf_get_pkgname "$bin")
                    src=$(dnf_get_source "$bin")
                fi
                if [ -z "$src" ]; then
                    src=$bin
                fi
            fi
            printf "%s,%s,%s,%s\n" $pkg $sym $bin $src
            array_add srcs $src
        done
        array_extend deps srcs
    done
    deps=($(array_uniq deps))
    array_extend $oarray deps
}

# __ship_get_repo_opts repoarray
__ship_get_repo_opts() {
    local repos=($(array_all $1))
    local opts=""
    for repo in ${repos[@]}; do
        if [ -z "$opts" ]; then
            opts="--repo $repo"
        else
            opts="$opts --repo $repo"
        fi
    done
    echo "$opts"
}

usage_builddep() {
    echo "builddep (bdep): Display the package build dependencies and all requires of dependent packages"
    echo ""
    echo "usage:"
    echo "    ${PROG} bdep PACKAGES -b|--bin reponame -s|--src reponame ..."
    echo ""
}

alias_def builddep bdep
# do_builddep pkgs -b|--bin reponame -s|--src reponame ...
do_builddep() {
    if [ $# -lt 1 ]; then
        usage_builddep; exit
    fi
    local ARGS=$(getopt -o "b:s:" -l "bin:src:" -n "builddep/bdep" -- "$@")
    eval set -- "${ARGS}"

    local binrepos=()
    local srcrepos=()
    while true; do
        case "${1}" in
            -b|--bin)
                shift; array_add binrepos ${1}; shift
                ;;
            -s|--src)
                shift; array_add srcrepos ${1}; shift
                ;;
            --)
                shift; break;
            ;;
        esac
    done

    local pkgs=$(get_list "$1")
    local array=()
    local pkgship=()
    for pkg in $pkgs; do
        array=(${array[@]} $pkg)
    done

    printf "%s,%s,%s,%s\n" "# Package" "# Requires-sym" "# Requires-bin" "# Requires-src"
    local srcopts=$(__ship_get_repo_opts srcrepos)
    dnf_set_global_option "$srcopts"
    __do_builddep array pkgship
    printf "%s,%s,%s,%s\n" "------" "------" "------" "------"
    local binopts=$(__ship_get_repo_opts binrepos)
    dnf_set_global_option "$binopts"
    array_extend array pkgship
    array=($(array_uniq array))
    __do_installdep array pkgship
    printf "\nDependencies:\n"
    array_uniq pkgship
}

